/*
 * interrupts.c
 *
 *  Created on: Jul 20, 2013
 *      Author: vszabo
 */

#include <avr/io.h>				// AVR device-specific IO definitions
#include <avr/interrupt.h>		// Interrupts

#include "globals.h"
#include "usart.h"
#include "adc.h"

extern struct pwm_parameters_struct pwm_parameters;
extern struct adc_results_struct adc_results;

extern volatile uint8_t rx_char;
extern volatile uint8_t rx_flag;

extern volatile uint8_t volume;

static struct adc_average_calac_struct
{
	uint16_t adc_partial_result;
	uint8_t index;
} adc_average_calac;

static uint8_t inverted_cycle = 0;


#ifdef PWM_IS_16_BITS
ISR(TIMER1_COMPA_vect)
{
	if(volume == 2)
	{
		OCR1A = pwm_parameters.low_length;
		/*
		 * Max volume duty cycle 50% ITs would be too frequent if it is not handled separately.
		 * Critical interrupt are: switch channel A off, and switch channel B on. */
		if(inverted_cycle)
		{
			PORTB &= ~(1u << PB1);    //off
			PORTC &= ~(1u << PC4);    //on (Low state active)
			inverted_cycle = 0;
		}
		else
		{
			PORTC |= (1u << PC4);    //off (High state is inactive)
			PORTB |= (1u << PB1);    //on
			inverted_cycle = 1;
		}
	}
	else
	{
// The following lines marked with '*' has been disabled due to requirement modification (inverted out not used when volume = 1 or 0)
//*		if(!inverted_cycle)
		{
			if(pwm_parameters.is_low_sate)
			{
				PORTB &= ~(1u << PB1); //off		//TODO: what happens at the end, diasable_pwm must handle it
				OCR1A = pwm_parameters.low_length;
				pwm_parameters.is_low_sate = 0;
//*				inverted_cycle = 1;			// After low state we can advance to inverted cycle
			}
			else
			{
				PORTB |= (1u << PB1);    //on
				OCR1A = pwm_parameters.high_lenght;
				pwm_parameters.is_low_sate = 1;
			}

		}
//*		else
//*		{
//*			if(pwm_parameters.is_low_sate)
//*			{
//*				PORTC |= (1u << PC4); //off (High state is inactive)
//*				OCR1A = pwm_parameters.low_length;
//*				pwm_parameters.is_low_sate = 0;
//*				inverted_cycle = 0;			// After low state we can advance to non-inverted cycle
//*			}
//*			else
//*			{
//*				PORTC &= ~(1u << PC4);    //on
//*				OCR1A = pwm_parameters.high_lenght;
//*				pwm_parameters.is_low_sate = 1;
//*
//*			}
//*
//*		}
	}

	//dbg_putchar('i');
	//PORTB ^= 1 << PB1;             /* LED1 toggle */
} //ISR(TIMER2_COMP_vect)
#endif

#ifdef PWM_IS_8_BITS
ISR(TIMER2_COMP_vect)
{
	if(pwm_parameters.is_low_sate)
	{
		PORTB &= ~(1<<PB1); //off
		OCR2 = pwm_parameters.low_length;
		pwm_parameters.is_low_sate = 0;
	}
	else
	{
		PORTB |= 1<<PB1;    //on
		OCR2 = pwm_parameters.high_lenght;
		pwm_parameters.is_low_sate = 1;
	}
	//PORTB ^= 1 << PB1;             /* LED1 toggle */
} //ISR(TIMER2_COMP_vect)
#endif

/*
 * Interrupt handling for ADC results. The average of four result is
 * calculated and returned to the main loop.
 */
ISR(ADC_vect)
{
	//dbg_putchar('.');
	//adc = ADCH;
	// TODO: optimization possible here
	if(adc_average_calac.index < 3)
	{
		adc_average_calac.adc_partial_result += ADCH;
		adc_average_calac.index++;
		start_adc();
		//dbg_putchar(';');
	}
	else
	{
		adc_average_calac.adc_partial_result += ADCH;
		adc_results.value = adc_average_calac.adc_partial_result  >> 2;		// Do the division
		adc_results.is_complete = 1;
		adc_average_calac.adc_partial_result = 0;
		adc_average_calac.index = 0;
		dbg_putchar('.');
	}

	// adc_results.value = ADCH;
	// adc_results.is_complete = 1;

} // ISR(ADC_vect)


/*
 * Read the USART. In this project it is only used for debug reasons.
 */
ISR(USART_RXC_vect)
{
	rx_char = UDR;
	rx_flag = 1;
	//dbg_putchar(rx_char);
}

/*
 * Capture bad ISR, if any. Can be used for debug.
 */
ISR(BADISR_vect)
{
    while(1) ;
    //dbg_putstring("\n\n!!! Error: BAD_ISR !!!\n\n\r");
} // ISR(BADISR_vect)
